﻿/*
Copyright (c) 2025 HeZongLun
NahidaProject is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan
PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

#pragma once

#include <string>
#include <map>
#include <vector>
#include <memory>
#include <functional>
#include <iostream>

#ifdef _WIN32
#define ExportSymbol  __declspec(dllexport)
#else
#define ExportSymbol
#endif

#ifdef _WIN32
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #pragma comment(lib, "ws2_32.lib")
#else
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
#endif

namespace NahidaProject {

	enum class ExportSymbol ClientHTTPMethod { GET, POST, PUT, DELETE_METHOD, HEAD, OPTIONS};

	enum class ExportSymbol ClientHTTPStatus {
		OK = 200,
		CREATED = 201,
		ACCEPTED = 202,
		NO_CONTENT = 204,
		BAD_REQUEST = 400,
		UNAUTHORIZED = 401,
		FORBIDDEN = 403,
		NOT_FOUND = 404,
		INTERNAL_SERVER_ERROR = 500,
		BAD_GATEWAY = 502,
		SERVICE_UNAVAILABLE = 503
	};

	struct ExportSymbol ClientHTTPResponse {
		int statusCode;
		std::string statusMessage;
		std::map<std::string, std::string> headers;
		std::string body;

		ClientHTTPResponse() : statusCode(0) {}
	};

	class ExportSymbol HTTPClient {
	private:
		std::string baseUrl;
		std::map<std::string, std::string> defaultHeaders;
		int timeoutSeconds;

#ifdef _WIN32
		WSADATA wsaData;
#endif

		// 辅助函数
		std::string MethodToString(ClientHTTPMethod method) const;
		std::string BuildHTTPRequest(const std::string& method, const std::string& url, const std::map<std::string, std::string>& headers, const std::string& body) const;
		ClientHTTPResponse ParseHTTPResponse(const std::string& response) const;
		std::pair<std::string, std::string> ParseURI(const std::string& url) const;
		bool InitalSocket();
		void CleanupSocket();

	public:
		HTTPClient(const std::string& baseUrl = "");
		~HTTPClient();
		HTTPClient(const HTTPClient&) = delete;
		HTTPClient& operator=(const HTTPClient&) = delete;

		void SetBaseURI(const std::string& url);
		void SetDefaultHeader(const std::string& key, const std::string& value);
		void SetTimeout(int seconds);

		ClientHTTPResponse Get(const std::string& url, const std::map<std::string, std::string>& headers = {});
		ClientHTTPResponse Post(const std::string& url, const std::string& body = "", const std::map<std::string, std::string>& headers = {});
		ClientHTTPResponse Put(const std::string& url, const std::string& body = "", const std::map<std::string, std::string>& headers = {});
		ClientHTTPResponse Delete(const std::string& url, const std::map<std::string, std::string>& headers = {});
		ClientHTTPResponse Request(ClientHTTPMethod method, const std::string& url, const std::string& body = "", const std::map<std::string, std::string>& headers = {});
		void AsyncRequest(ClientHTTPMethod method, const std::string& url, std::function<void(const ClientHTTPResponse&)> callback, const std::string& body = "", const std::map<std::string, std::string>& headers = {});
	};

}
